package com.fzy.common.excel.export;

import com.fzy.common.bean.ReflectionUtil;
import com.fzy.common.date.DateUtil;
import com.fzy.common.util.StringUtil;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

/**
 * 导出数据到excel文件中
 *
 * @author 冯赵杨
 * @date 2012-11-30
 */
public class ExcelExportUtil {
    private ExcelExportUtil() {
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(ExcelExportUtil.class);

    /**
     * 通过excel模板导出excel (建议使用模板导出，方便自定义表格样式)
     *
     * @param response
     * @param param
     * @throws Exception
     * @author 冯赵杨
     * @date 2016年11月15日 下午4:36:39
     */

    public static void exportTemplateExcel(HttpServletResponse response, ExportParam param) throws Exception {

        // excel模板路径
        File fi = new File(param.getExcelTemplatePath());

        // 读取excel模板
        Workbook wb = WorkbookFactory.create(new BufferedInputStream(new FileInputStream(fi)));

        // 获取第一个sheet页
        Sheet sheet = wb.getSheetAt(0);

        // 创建单元格样式
        CellStyle cellStyle = contentStyle(wb);

        // 创建内容
        createDataRow(sheet, cellStyle, param);

        // 响应输出
        response(response, wb, param.getExcelName());

    }

    /**
     * 不通过excel模板导出excel
     *
     * @param response
     * @param param
     * @throws Exception
     * @author 冯赵杨
     * @date 2016年11月15日 下午4:36:39
     */
    public static void exportExcel(HttpServletResponse response, ExportParam param) throws Exception {
        Workbook wb;
        if (StringUtil.isNotNull(param.getDataList())) {
            wb = new SXSSFWorkbook(param.getDataList().size());
        } else {
            wb = new SXSSFWorkbook(1);
        }

        // 获取第一个sheet页
        Sheet sheet = wb.createSheet(param.getExcelName());

        int n = 0;
        if (StringUtil.isNotNull(param.getBigTitle())) {
            // 创建大标题
            createBigTitle(wb, sheet, 0, param.getBigTitle(), param.getSmallTitle().length - 1);
            n = 1;
        }

        // 创建各列小标题
        createSmallTitles(wb, sheet, n, param.getSmallTitle(), param.getColWidths());

        // 创建单元格样式
        CellStyle cellStyle = contentStyle(wb);

        // 创建内容
        createDataRow(sheet, cellStyle, param);

        // 响应输出
        response(response, wb, param.getExcelName());

    }

    /**
     * @desc 创建数据行
     * @author 冯赵杨
     * @date 2016年11月17日 下午4:23:35
     */
    public static void createDataRow(Sheet sheet, CellStyle cellStyle, ExportParam param) throws Exception {
        List<Map<String, Object>> list = param.getDataList();

        for (int i = 0; i < list.size(); i++) {
            int rowNo = i + param.getStartRow();

            Row newRow = sheet.createRow(rowNo);// 从第startRow行开始创建

            Map<String, Object> map = list.get(i);

            for (int j = 0; j < param.getFieldKeys().length; j++) {
                String fieldKey = param.getFieldKeys()[j];
                Object fieldValue = map.get(fieldKey);

                // 执行java方法
                fieldValue = excuteMethod(param.getMethodMap(), fieldKey, fieldValue, map);

                // 数据字典转换
                fieldValue = convertCode(param.getCodeMap(), fieldKey, fieldValue);

                String value = "";

                if (fieldValue != null) {
                    value = fieldValue.toString();
                }

                // 创建单元格 从第startCell列开始创建
                Cell headerCell = newRow.createCell(j + param.getStartCell());
                headerCell.setCellValue(value);
                headerCell.setCellStyle(cellStyle);

                //sheet.setColumnWidth(j + param.getStartCell(), value.length() * 512);//设置列宽
            }
        }

        // 给单元格增加下拉框
        if (param.getAddValidationCell() != null) {
            int[] valCells = param.getAddValidationCell();

            for (int item : valCells) {
                String text = param.getValidationDataMap().get(item);

                sheet.addValidationData(setDataValidation(sheet, text, param.getStartRow(),
                        list.size() + param.getStartRow(), item, item));
            }
        }
    }

    /**
     * @param codeMap    所有字典Map集合
     * @param fieldKey   某个字段
     * @param fieldValue 字段值
     * @Desc 数据字典转换
     * @author 冯赵杨
     * @date 2016年11月14日 下午3:47:50
     */
    public static Object convertCode(Map<String, String> codeMap, String fieldKey, Object fieldValue) {
        String codeName = codeMap.get(fieldKey);
        if ("".equals(codeName) && fieldValue != null) {
            String[] ary = codeName.split(",");
            for (String str : ary) {
                //日期格式转换
                if (DateUtil.checkDateFormat(str)) {
                    return DateUtil.date2String(fieldValue, str);
                }

                String[] oneAry = str.split("=");
                if (oneAry[0].equals(String.valueOf(fieldValue))) {
                    return oneAry[1];
                }
            }
        }

        return fieldValue;
    }

    /**
     * @param codeName 字典集合
     * @param valueStr 字段值
     * @Desc 数据字典转换
     * @author 冯赵杨
     * @date 2016年11月14日 下午3:47:50
     */
    public static String convertCode(String codeName, String valueStr) {
        if (codeName != null && codeName != "") {

            String[] ary = codeName.split(",");
            for (String str : ary) {
                String[] oneAry = str.split("=");
                if (valueStr.equals(oneAry[0])) {
                    return oneAry[1];
                }
            }
        }

        return valueStr;
    }

    /**
     * @desc 反射执行java方法
     * @author 冯赵杨
     * @date 2016年11月14日 下午3:48:04
     */
    public static Object excuteMethod(Map<String, String> methodMap, String fieldKey, Object fieldValue,
                                      Map<String, Object> map) throws Exception {
        String method = methodMap.get(fieldKey);

        if (method != null && !"".equals(method)) {
            String[] methods = method.split(";");
            int len = methods.length;

            String className = methods[0];
            String methodName = methods[1];

            // 当值不为空 调用反射方法
            if (fieldValue != null && len == 2) {
                return ReflectionUtil.invokeStaticMethod(className, methodName, fieldValue);
            }

            if (len == 3) {
                String params = methods[len - 1];
                return ReflectionUtil.invokeStaticMethod(className, methodName, map, params);
            }
        }

        return fieldValue;
    }

    /**
     * @desc 响应输出
     * @author 冯赵杨
     * @date 2016年11月14日 下午4:04:46
     */
    public static void response(HttpServletResponse response, Workbook wb, String fileName) {
        ServletOutputStream stream = null;
        try {
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/msexcel;charset=UTF-8");
            response.setHeader("Content-disposition",
                    "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            stream = response.getOutputStream();
            wb.write(stream);
        } catch (Exception e) {
            LOGGER.error("导出excel文件出错", e);
        } finally {
            try {
                if (stream != null) {
                    stream.flush();
                    stream.close();
                }
            } catch (Exception e) {
                LOGGER.error("关闭流出错", e);
            }
        }
    }

    /**
     * 生成大标题样式和内容
     *
     * @param sheet 页面
     * @param row   第几行生成
     * @param name  大标题名称
     * @param cols  共几列 供合并
     */
    public static void createBigTitle(Workbook wb, Sheet sheet, int row, String name, int cols) {
        // 大标题样式
        CellStyle headerStyle = wb.createCellStyle();
        headerStyle.setAlignment(HorizontalAlignment.CENTER);
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        // 设置颜色为白色
        headerStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex());

        Font hfont = wb.createFont();
        hfont.setFontHeight((short) 500);
        hfont.setBold(true);
        headerStyle.setFont(hfont);

        // 生成大标题值
        Row headerRow = sheet.createRow(row);
        Cell headerCell = headerRow.createCell(0);
        headerCell.setCellValue(name);
        // 合并单元格
        sheet.addMergedRegion(new CellRangeAddress(row, 0, row, cols));
        headerCell.setCellStyle(headerStyle);
    }

    /**
     * 生成各列小标题样式和内容
     *
     * @param sheet     页面对象
     * @param row       在第几行生成
     * @param names     各列名称集合
     * @param colWidths 对应各列的宽度
     */
    public static void createSmallTitles(Workbook wb, Sheet sheet, int row, String[] names, int[] colWidths) {
        // 文字样式
        CellStyle cellStyle = wb.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        // 设置颜色为灰色
        cellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.GREY_25_PERCENT.getIndex());

        // 标题样式
        Font tfont = wb.createFont();
        tfont.setFontHeight((short) 200);
        tfont.setBold(true);

        cellStyle.setFont(tfont);
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBottomBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setLeftBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setRightBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setTopBorderColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());

        Row rowHead = sheet.createRow(row);
        //rowHead.setHeight((short) height);

        for (int i = 0; i < names.length; i++) {
            Cell cellHead = rowHead.createCell(i);
            cellHead.setCellStyle(cellStyle);
            cellHead.setCellValue(names[i]);
            if (colWidths != null && colWidths[i] != 0) {
                sheet.setColumnWidth(i, colWidths[i] * 256);
            }
        }
    }

    /**
     * 设置单元格内容样式
     */
    public static CellStyle contentStyle(Workbook wb) {
        CellStyle contentStyle = wb.createCellStyle();
        contentStyle.setAlignment(HorizontalAlignment.CENTER);
        contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // contentStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        // contentStyle.setFillForegroundColor(HSSFColor.WHITE.index); //设置颜色

        /*
         * Font hfont = wb.createFont();
         * hfont.setFontHeight((short) 100);
         * hfont.setBoldweight((short) 2);
         * contentStyle.setFont(hfont);
         */

        return contentStyle;
    }

    /**
     * @Desc 单元格设置下拉菜单
     * @author 冯赵杨
     * @date 2016年11月16日 下午3:35:56
     */
    public static DataValidation setDataValidation(Sheet sheet, String text, int firstRow, int endRow, int firstCol,
                                                   int endCol) {

        DataValidationHelper helper = sheet.getDataValidationHelper();
        // 加载下拉列表内容
        DataValidationConstraint constraint = helper.createExplicitListConstraint(text.split(","));
        // DVConstraint constraint = new DVConstraint();
        // constraint.setExplicitListValues(textList);

        // 设置数据有效性加载在哪个单元格上。
        // 四个参数分别是：起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);

        // 数据有效性对象
        return helper.createValidation(constraint, regions);
    }

}
